package xconf

import (
	"bufio"
	"io"
	"strings"

	. "gitee.com/shuohe/xutils"
	"gitee.com/shuohe/xutils/xerror"
)

var g_scanner *bufio.Scanner
var g_lineNumber int
var g_indent int
var g_line string
var g_scanErr error

var g_identifier string

/*
 * 从 start 开始，跳过空格字符，返回下一个非空格字符的下标。
 */
func skipSpace(line string, start int) int {
	line_len := len(line)
	for start < line_len && line[start] == ' ' {
		start++
	}

	return start
}

/*
 * 从 start 开始，跳过空白字符，返回下一个非空白字符的下标。
 */
func skipWhite(line string, start int) int {
	line_len := len(line)
	for start < line_len && IsWhite(line[start]) {
		start++
	}

	return start
}

/*
 * 从 start 开始，读取一个标识符，返回下次读取的位置。
 */
func getIdentifier(line string, start int) (int, error) {
	line_len := len(line)
	// 跳过空白
	start = skipWhite(line, start)
	if start >= line_len {
		return -1, xerror.New("Unexpected end of text.")
	}

	// 第一个字符必须是字母
	if !IsAlpha(line[start]) {
		return -1, xerror.New("Identifier should start with a English letter.")
	}

	// 扫描直到遇到非字母数字
	stop := start + 1
	for stop < line_len && IsAlphaDigit(line[stop]) {
		stop++
	}

	g_identifier = line[start:stop]

	return stop, nil
}

/**
 * 吃掉下一个非空字符，并要求一定是 ':' 或 '='。
 */
func matchSeperator(line string, start int) (int, error) {
	line_len := len(line)
	start = skipWhite(line, start)
	if start >= line_len {
		return -1, xerror.New("Unexpected end of text.")
	}

	if line[start] != ':' && line[start] != '=' {
		return -1, xerror.Newf("Expect ':' or '=', but get '%c'.", line[start])
	}

	return start + 1, nil
}

/**
 * 去除字符串中 '#' 之后直到末尾的注释内容。
 */
func removeComment(line string) string {
	if i := strings.IndexByte(line, '#'); i >= 0 {
		line = line[:i]
	}

	return line
}

/*
 * 计算缩进层次，然后去掉缩进用的空格。
 */
func calcAndRemoveIndent(line string) (int, string, error) {
	var i int

	line_len := len(line)
	for i = 0; i < line_len; i++ {
		c := line[i]
		if c != ' ' {
			break
		}
	}

	indent := i / 4
	if i-(4*indent) > 0 {
		return -1, "", xerror.New("Incorrect indent.")
	}

	return indent, line[i:], nil
}

func scanNextLine() (int, string, error) {
	for {
		g_lineNumber++
		if !g_scanner.Scan() {
			if err := g_scanner.Err(); err != nil {
				return -1, "", xerror.Wrap(err, "Failed due to error.")
			} else {
				return -1, "", io.EOF
			}
		}

		line := removeComment(g_scanner.Text())
		if line == "" {
			continue
		}

		return calcAndRemoveIndent(line)
	}
}
